package com.hundsun.cprs.yyt.service.match.service.impl;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import com.hundsun.cprs.yyt.service.trade.domain.TradeCancelFlow;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import com.hundsun.cprs.yyt.service.match.core.TradeCenter;
import com.hundsun.cprs.yyt.service.match.dto.EntrustRequest;
import com.hundsun.cprs.yyt.service.match.service.ServiceLocate;
import com.hundsun.cprs.yyt.service.match.service.TradeMatchInnerService;
import com.hundsun.cprs.yyt.service.rabbit.RoutingKeyConstants;
import com.hundsun.cprs.yyt.service.system.domain.BussinessParam;
import com.hundsun.cprs.yyt.service.system.enums.EnumBussinessParam;
import com.hundsun.cprs.yyt.service.system.service.BusinessParamHelper;
import com.hundsun.cprs.yyt.service.system.service.BussinessParamService;
import com.hundsun.cprs.yyt.service.trade.domain.TradeEntrust;
import com.hundsun.cprs.yyt.service.trade.domain.TradeRollbackFlow;
import com.hundsun.cprs.yyt.service.trade.domain.query.TradeEntrustQuery;
import com.hundsun.cprs.yyt.service.trade.enums.EnumEntustBSType;
import com.hundsun.cprs.yyt.service.trade.enums.EnumEntustFrom;
import com.hundsun.cprs.yyt.service.trade.enums.EnumTradeEntrustStatus;
import com.hundsun.cprs.yyt.service.trade.enums.EnumTradeStatus;
import com.hundsun.cprs.yyt.service.trade.service.TradeEntrustService;
import com.hundsun.cprs.yyt.util.TradeEntrustBeanUtil;

@Service
public class TradeMatchInnerServiceImpl implements TradeMatchInnerService {
    
    private static Log logger = LogFactory.getLog(TradeMatchInnerServiceImpl.class);

    private Map<String, TradeCenter> centers = new ConcurrentHashMap<String,TradeCenter>();
    
    private Executor asyncMatchExecutor = Executors.newFixedThreadPool(10);
    
    @Autowired
    private TradeEntrustService tradeEntrustService;
    
    @Autowired
    private BussinessParamService bussinessParamService;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void handleEntrust(EntrustRequest entrust) {
        Assert.notNull(entrust, "委托信息必填");
        Assert.notNull(entrust.getId(), "委托单信息必填");
        TradeEntrust tradeEntrust = tradeEntrustService.getTradeEntrustById(entrust.getId());
        if (tradeEntrust == null) {
            throw new IllegalArgumentException("委托订单不存在");
        }
        
        // 获取商品的匹配引擎
        TradeCenter tradeCenter = centers.get(tradeEntrust.getGoodsCode());
        if (tradeCenter == null) {
            //没有，则初始化匹配引擎
            tradeCenter = initTradeCenter(tradeEntrust.getGoodsCode());
        }
        //根据委托主键ID，更新委托端为已上报
        commitEntrust(tradeEntrust);
        // 进入匹配中心
        tradeCenter.addEntrust(TradeEntrustBeanUtil.copyPropertiesFrom(tradeEntrust));
    }
    
    protected TradeEntrust commitEntrust(TradeEntrust tradeEntrust){
        if (EnumTradeEntrustStatus.INIT.getStatus() != tradeEntrust.getEntrustStatus()) {
            throw new IllegalArgumentException("委托订单已上报");
        }
        
        try {
            if (tradeEntrust.getSurplusEntrustAmount().compareTo(tradeEntrust.getEntrustAmount()) < 0) {
                tradeEntrust.setEntrustStatus(EnumTradeEntrustStatus.PART_DEALT.getStatus());
            } else {
                tradeEntrust.setEntrustStatus(EnumTradeEntrustStatus.COMMITED.getStatus());
            }
            tradeEntrust.setStatus(EnumTradeStatus.COMMITED.getStatus());
            setTradeEntrustExpireDate(tradeEntrust);
            tradeEntrust = tradeEntrustService.updateEntrustStatus(tradeEntrust,EnumTradeEntrustStatus.INIT.getStatus());
        } catch (Exception e) {
            logger.warn("委托单[" + tradeEntrust.getId() +"]已上报,无需重复上报",e);
            throw new IllegalArgumentException("委托订单已上报"); 
        }
        
        //发送上报成功消息
        rabbitTemplate.convertAndSend(RoutingKeyConstants.YYT_TRADE_ENTRUST_COMMIT_SUCCESS, tradeEntrust);
        return tradeEntrust;
    }
    
    protected TradeCenter initTradeCenter(String stockCode){
        TradeCenter tradeCenter = null;
        final List<TradeEntrust> expireEntrust = new ArrayList<TradeEntrust>();
        synchronized (this.centers) {
            //二次校验
            tradeCenter = centers.get(stockCode);
            if (tradeCenter == null) {
                tradeCenter = new TradeCenter(stockCode, asyncMatchExecutor);
                initSystemConfig(tradeCenter);
                loadHistoryEntrust(tradeCenter,expireEntrust);
                tradeCenter.start();
                centers.put(stockCode, tradeCenter);
            }
        }
        //异步处理过期的订单
        asyncMatchExecutor.execute(new Runnable() {
            public void run() {
                if (!expireEntrust.isEmpty()) {
                    if (logger.isInfoEnabled()) {
                        logger.info("有过期订单，更新订单为已撤销" + expireEntrust.size());
                    }
                    ServiceLocate.getTradeMatchService().cancelEntrust(expireEntrust);
                }
            }
        });
        return tradeCenter;
    }
    
    protected void initSystemConfig(TradeCenter tradeCenter){
        //获取系统权重配置
        BussinessParam weightParam = bussinessParamService.getBussinessParamByKey(EnumBussinessParam.MATCH_NEW_WEIGHT.getCode());
        if (weightParam != null) {
            Integer[] systemWeight = parseSystemParam(weightParam.getBussinessValue());
            if (systemWeight != null) {
                tradeCenter.getMatching().getSellQueue().resetWeight(systemWeight[1], systemWeight[0]);
            }
        }
        BussinessParam matchBuyNewRateParam = bussinessParamService.getBussinessParamByKey(EnumBussinessParam.MATCH_BUY_NEWUSER_RATE.getCode());
        if (matchBuyNewRateParam != null) {
            try {
                tradeCenter.setMatchBuyNewUserRate(Integer.parseInt(matchBuyNewRateParam.getBussinessValue()));
            } catch (NumberFormatException e) {
                logger.error("匹配新用户商品数比例解析错误,采用默认值");
            }
        }
    }
    
    protected void loadHistoryEntrust(TradeCenter tradeCenter,List<TradeEntrust> expireEntrust) {
        if (logger.isInfoEnabled()) {
            logger.info(MessageFormat.format("商品[{0}]开始重新挂单...", tradeCenter.getStockCode()));
        }
        long currentTime = System.currentTimeMillis();
        //加载未完成的委售单
        TradeEntrustQuery query = new TradeEntrustQuery();
        query.setEntrustBs(EnumEntustBSType.SELL.getCode().toString());
        query.setGoodsCode(tradeCenter.getStockCode());
        query.setEntrustStatusArray(new Integer[]{EnumTradeEntrustStatus.COMMITED.getStatus(),EnumTradeEntrustStatus.PART_DEALT.getStatus()});
        List<TradeEntrust> tradeEntrustList = tradeEntrustService.selectByList(query);
        if (tradeEntrustList != null) {
            if (logger.isInfoEnabled()) {
                logger.info(MessageFormat.format("商品[{0}]重新挂售单{1}", tradeCenter.getStockCode(),tradeEntrustList.size()));
            }
            for(TradeEntrust tradeEntrust : tradeEntrustList){
                if (tradeEntrust.getExpireDate() != null && tradeEntrust.getExpireDate().getTime() <= System.currentTimeMillis()) {
                    long entrustAmount = tradeEntrust.getEntrustAmount();
                    long surplusEntrustAmount = tradeEntrust.getSurplusEntrustAmount();
                    if (entrustAmount > surplusEntrustAmount) {
                        tradeEntrust.setEntrustStatus(EnumTradeEntrustStatus.PART_CANCELED.getStatus());
                    } else {
                        tradeEntrust.setEntrustStatus(EnumTradeEntrustStatus.CANCELED.getStatus());
                    }
                    expireEntrust.add(tradeEntrust);
                } else {
                    tradeCenter.addEntrust(TradeEntrustBeanUtil.copyPropertiesFrom(tradeEntrust));
                }
            }
            if (logger.isInfoEnabled()) {
                logger.info(MessageFormat.format("商品[{0}]已过期挂售单{1}", tradeCenter.getStockCode(),expireEntrust.size()));
            }
        }
        //重新加载并匹配未处理的委买单
        query.setEntrustBs(EnumEntustBSType.BUY.getCode().toString());
        tradeEntrustList = tradeEntrustService.selectByList(query);
        if (tradeEntrustList != null) {
            if (logger.isInfoEnabled()) {
                logger.info(MessageFormat.format("商品[{0}]重新挂买单{1}", tradeCenter.getStockCode(),tradeEntrustList.size()));
            }
            for(TradeEntrust tradeEntrust : tradeEntrustList){
                tradeCenter.addEntrust(TradeEntrustBeanUtil.copyPropertiesFrom(tradeEntrust));
            }
        }
        if (logger.isInfoEnabled()) {
            logger.info(MessageFormat.format("商品[{0}]挂单初始化结束,耗时{1}ms", tradeCenter.getStockCode(),(System.currentTimeMillis() - currentTime)));
        }
    }
    
    private void setTradeEntrustExpireDate(TradeEntrust entrust){
        if (entrust.getEntrustTime()==null){
            entrust.setEntrustTime(entrust.getGmtCreate());
        }
        if (EnumEntustFrom.FROM_USER.getCode() == entrust.getEntrustFrom()) {
            Integer expireDays = BusinessParamHelper.getIntValue(bussinessParamService, EnumBussinessParam.ENTRUST_SELL_EXPIRE_DAYS,15);
            if (StringUtils.equals(EnumEntustBSType.SELL.getCode().toString(),entrust.getEntrustBs())) {
                entrust.setExpireDate(DateUtils.round(DateUtils.addDays(entrust.getEntrustTime(), expireDays),Calendar.DAY_OF_MONTH));
            } else {
                // 买单无限期
                entrust.setExpireDate(DateUtils.addYears(entrust.getEntrustTime(), 50));
            }
        } else {
            // 平台委托单不受限，无限制
            entrust.setExpireDate(DateUtils.addYears(entrust.getEntrustTime(), 50));
        }
    }
    
    @Override
    public Collection<TradeCenter> getHandleTradeCenters() {
        return new ArrayList<TradeCenter>(centers.values());
    }

    @Override
    public void resetMatchingSellQueueWeight(String matchNewWeight) {
        Integer[] weight = parseSystemParam(matchNewWeight);
        if (weight == null) {
            return;
        }
        
        Iterator<TradeCenter> it = centers.values().iterator();
        while(it.hasNext()){
            TradeCenter center = it.next();
            //实时更新权重
            center.getMatching().getSellQueue().resetWeight(weight[1], weight[0]);
            
            if (logger.isDebugEnabled()) {
                logger.debug(MessageFormat.format("匹配中心[{0}]新老用户权重更新为:新{1}:老{2}",center.getStockCode(),weight[1],weight[0]));
            }
        }
    }
    
    @Override
    public void resetMatchBuyNewUserRate(String matchBuyNewUserRate) {
        Iterator<TradeCenter> it = centers.values().iterator();
        while(it.hasNext()){
            TradeCenter center = it.next();
            //实时更新权重
            try {
                center.setMatchBuyNewUserRate(Integer.parseInt(matchBuyNewUserRate));
            } catch (NumberFormatException e) {
                logger.error("匹配新用户商品数比例解析错误,更新失败");
                break;
            }
            
            if (logger.isDebugEnabled()) {
                logger.debug(MessageFormat.format("匹配中心[{0}]匹配新用户商品数占比更新为:{1}",center.getStockCode(),matchBuyNewUserRate));
            }
        }
    }
    
    private Integer[] parseSystemParam(String matchNewWeight){
        if (matchNewWeight == null) {
            return null;
        }
        
        String[] weightArray = StringUtils.split(matchNewWeight, ":");
        if (weightArray.length != 2) {
            logger.error("系统参数" + EnumBussinessParam.MATCH_NEW_WEIGHT.getCode() + "格式有误:" + matchNewWeight);
            return null;
        }
        Integer[] weight = new Integer[2];
        try {
            weight[0] = Integer.parseInt(weightArray[0]);
            weight[1] = Integer.parseInt(weightArray[1]);
            
            return weight;
        } catch (NumberFormatException e) {
            logger.error("系统参数" + EnumBussinessParam.MATCH_NEW_WEIGHT.getCode() + "格式有误:" + matchNewWeight);
            return null;
        }
    }

    @Override
    public void reInitTradeCenter(String stockCode) {
       //移除老匹配中心
       TradeCenter center = centers.remove(stockCode);
       if (center != null) {
           center.release();
       }
       initTradeCenter(stockCode);
    }

    public Executor getAsyncMatchExecutor() {
        return asyncMatchExecutor;
    }

    @Override
    public void rollbackEntrust(TradeRollbackFlow applyRollback) {
        Assert.notNull(applyRollback, "撤回申请信息必填");
        TradeEntrust tradeEntrust = tradeEntrustService.getTradeEntrustById(applyRollback.getEntrustId());
        
        // 获取商品的匹配引擎
        TradeCenter tradeCenter = centers.get(tradeEntrust.getGoodsCode());
        if (tradeCenter == null) {
            tradeCenter = initTradeCenter(tradeEntrust.getGoodsCode());
        }
        
        // 进入匹配中心处理撤回
        tradeCenter.rollbackEntrust(TradeEntrustBeanUtil.copyPropertiesFrom(tradeEntrust), applyRollback);
    }

    @Override
    public void cancelEntrust(TradeCancelFlow cancelFlow) {
        Assert.notNull(cancelFlow, "撤单信息必填");

        TradeEntrust tradeEntrust = tradeEntrustService.getTradeEntrustById(cancelFlow.getEntrustId());
        // 获取商品的匹配引擎
        TradeCenter tradeCenter = centers.get(tradeEntrust.getGoodsCode());
        if (tradeCenter == null) {
            tradeCenter = initTradeCenter(tradeEntrust.getGoodsCode());
        }

        tradeCenter.cancelEntrust(TradeEntrustBeanUtil.copyPropertiesFrom(tradeEntrust), cancelFlow);

    }
}
